package com.i72.governance.init;

import com.i72.basic.context.GlobalContext;
import com.i72.basic.tools.BeanHelper;
import com.i72.basic.tools.YAMLHelper;
import com.i72.governance.service.bean.ConfigFlushEvent;
import com.i72.governance.service.helper.ServiceHelper;
import org.apache.commons.collections.IteratorUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.ScannedGenericBeanDefinition;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.env.PropertiesPropertySource;
import org.springframework.core.env.PropertySource;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Properties;
import java.util.stream.Collectors;


@Component
public class GovernanceConfigFlush implements ApplicationListener<ConfigFlushEvent> {



    private final static String ORG_SPRINGFRAMEWORK="org.springframework";
    private final static Integer FLUSH_NUM = 10;      //刷新个数
    //private final static Long WAIT_TIME = 10*1000L;   //延迟刷新时间
    private final static Long SUSPEND_TIME = 100L;    //刷新途中暂停时间
    private final static Long COOLING_TIME = 120*1000L; //强制冷却时间
    private static Boolean SIGN = false;        //防重复执行标识
    private final static Integer STEP3 = 3;
    private final static Integer STEP5 = 5;


    @Autowired
    private ThreadPoolTaskExecutor threadPoolTaskExecutor;
    private ApplicationContext applicationContext;
    private Properties properties;

    @Autowired
    private ServiceHelper serviceHelper;

    @Override
    public void onApplicationEvent(ConfigFlushEvent event) {


        if(!SIGN) {
            GenericApplicationContext app = (GenericApplicationContext)event.getApplicationContext();


            String config = event.getConfig();
            Properties properties = YAMLHelper.yamlToProperties(config);

            //需要更新 spring Environment
            PropertySource p = new PropertiesPropertySource("updateConfig"+System.currentTimeMillis(), properties);

            List<PropertySource> ps = IteratorUtils.toList(app.getEnvironment().getPropertySources().iterator());

            if(ps!=null && ps.size()>0) {
                PropertySource lastPropertySource = ps.get(ps.size()-1);
                app.getEnvironment().getPropertySources().remove(lastPropertySource.getName());
            }
            //新值追加到最后，防止动态注册的bean，得不到最新的配置，虽然这种情况是极端的，但是还是预防一下。
            //
            app.getEnvironment().getPropertySources().addLast(p);


            this.properties = properties;
            this.applicationContext = app;


                threadPoolTaskExecutor.execute(() -> {
                    try {
                        SIGN = true;
                        Integer timeSpan = new Integer(GlobalContext.getConfigRefreshTimespan());
                        if(timeSpan>10){
                            timeSpan = timeSpan*STEP3;
                        }else{
                            timeSpan = timeSpan*STEP5;
                        }
                        Thread.sleep(timeSpan*1000L);
                        flushTask();
                        SIGN = false;
                    } catch (Exception e) {
                        //异常时候也要解除执行标识
                        SIGN = false;
                    }
                });
        }

        threadPoolTaskExecutor.execute(()->{
            try {
                Thread.sleep(COOLING_TIME);
            }catch (Exception e){
            }
            SIGN = false;
        });

    }

    private void flushTask() throws Exception{

        GenericApplicationContext app = (GenericApplicationContext)this.applicationContext;
        String [] beanNames = app.getBeanDefinitionNames();

        //BeanDefinition beanDefinition = app.getBeanDefinition("");

        //先从包名上过滤掉大部分spring bean 因为他们不需要刷新
        //只刷新我们项目的类

        List<String> flushBean = new ArrayList<>();

        for(String name:beanNames){
            if(name.contains(ORG_SPRINGFRAMEWORK) || !name.contains(GlobalContext.getAPPPackage())){
                continue;
            }

            BeanDefinition beanDefinition = app.getBeanDefinition(name);

            if(beanDefinition instanceof ScannedGenericBeanDefinition || beanDefinition instanceof GenericBeanDefinition) {

                /*
                 * 如果一个类加上了@ComponentScan注解，在扫描到那些被标记了@Component、@Controller、@Service等注解的 bean 类时，
                 * 为这些类生成的 bean definition 是 ScannedGenericBeanDefinition。
                 * 这样的bean被认为是我们需要刷新的
                 */

                /*
                * GenericBeanDefinition 为动态注册的bean
                * 这个条件为后加入，因为可能会AOP一个类，此时这个类是动态生成并且注册到容器的
                *
                */

                flushBean.add(name);
            }

            /**
             if(beanDefinition instanceof ConfigurationClassBeanDefinition){
             //如果一个 bean 是通过@Bean标记产生的，那么这个 bean 的 bean definition 将是 ConfigurationClassBeanDefinition 类型
             //但是这样的类，都编程获得的，基本上也都是自己管理注入等，所以不需要理会。
             }*/


        }



        //开启线程刷新
        //为了保持项目稳定，分页刷新。
        //每次开独立的线程去运行

        int size = flushBean.size();
        int pageSize  = size% FLUSH_NUM== 0 ? size/ FLUSH_NUM : size/ FLUSH_NUM+ 1 ;

        if(pageSize>0){

            for(int i=0;i<pageSize;i++){

                List<String> current = flushBean.stream()
                        .skip(i*FLUSH_NUM)
                        .limit(FLUSH_NUM).collect(Collectors.toList());

                flush(current);

                //每刷新一次，暂停0.1
                Thread.sleep(SUSPEND_TIME);

            }
        }
    }

    private void flush(List<String> beanNames){

        //每次刷新的bean，新开一个线程处理
        threadPoolTaskExecutor.execute(()->{

            for (String name:beanNames){

                Object o = this.applicationContext.getBean(name);

                List<Field> fieldList = BeanHelper.findField(o, Value.class);

                if(fieldList!=null && fieldList.size()>0){

                    //证明需要改配置，此时，锁住当前对象。
                    //避免流量进入

                    synchronized (o){
                        for(Field f: fieldList){

                            //获取注解值，得到对象的配置key

                            //修改字段值，从新的配置中获取

                            //完成刷新，释放锁

                            Value valueAttr = f.getAnnotation(Value.class);

                            if(valueAttr!=null && !StringUtils.isEmpty(valueAttr.value())){
                                String key = valueAttr.value().trim();
                                String key2 = key.replace("${","").replace("}","");
                                f.setAccessible(true);
                                try {

                                    if(properties.keySet().contains(key2)){
                                        Object value = f.get(o);
                                        if(value!=null && !value.toString().equals(properties.getProperty(key))){
                                            //不相等，刷新
                                            f.set(o,properties.getProperty(key2));
                                        }
                                    }

                                }catch (Exception e){

                                }

                            }

                        }
                    }

                }
            }

        });
    }

}
